package service.impl;

import client.SmsClient;
import common.consts.CaptchaUsage;
import common.consts.Consts;
import common.consts.YesNo;
import dao.AccountDao;
import dao.CaptchaDao;
import domain.Account;
import domain.Captcha;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import service.CaptchaService;
import service.ServiceResponse;

import java.util.Date;

/**
 * Created by aceyin on 2015/6/8 0008.
 */
@Service
public class CaptchaServiceImpl extends ServiceBase implements CaptchaService {
    private static final Logger LOGGER = LoggerFactory.getLogger(CaptchaServiceImpl.class);
    // TOKEN format: DJ_CAPTCHA_<USAGE>_<MOBILE>_<CAPTCHA>
    private static final String TOKEN_FORMAT = "DJ_CAPTCHA_%s_%s_%s";
    private static final String SMS_TEMPLATE = "您的验证码是：%s。";

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private CaptchaDao captchaDao;

    @Autowired
    private SmsClient smsClient;

    /**
     * @param mobile
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public ServiceResponse sendSmsCaptcha(String mobile, String usage) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Start to send captcha: {mobile=" + mobile + ",usage=" + usage + "}");
        }
        Account account = accountDao.findByUsername(mobile);

        if (CaptchaUsage.REGISTER.name().equalsIgnoreCase(usage)) {
            if (account != null) {
                return ServiceResponse.fail(Consts.i18nKey.ACCOUNT_ALREADY_EXIST);
            }
        } else if (CaptchaUsage.RESET_PWD.name().equalsIgnoreCase(usage)) {
            if (account == null) {
                return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_USER);
            }
        } else {
            return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_CAPTCHA_USAGE);
        }

        boolean overFrequent = isOverFrequent(mobile, usage);
        if (overFrequent) {
            return ServiceResponse.fail(Consts.i18nKey.SEND_SMS_OVER_FREQUENT);
        }

        int i = RandomUtils.nextInt(100000, 999999);
        String isTest = System.getProperty(Consts.KEY_IS_TEST_ENV);
        if (StringUtils.isNotBlank(isTest)) {
            i = Consts.DEFAULT_TEST_CAPTCHA;
        }
        String val = String.valueOf(i);

        String content = String.format(SMS_TEMPLATE, val);
        boolean b = smsClient.send(mobile, content);
        if (b == false) {
            return ServiceResponse.fail(Consts.i18nKey.SEND_SMS_CAPTCHA_FAILED);
        }

        Captcha captcha = new Captcha();
        captcha.setCaptcha(val);
        captcha.setMobile(mobile);
        captcha.setSendTime(new Date());
        captcha.setUsage(usage);
        captcha.setUsed(YesNo.No.charVal);

        captchaDao.insert(captcha);

        String token = createToken(mobile, usage, val);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Send captcha success : {mobile=" + mobile + ",usage=" + usage + ",token=" + token + ",captcha=" + val + "}");
        }
        return ServiceResponse.success().with("token", token);
    }

    /**
     * 检查发送频率是否过快
     *
     * @param mobile
     * @param usage
     * @return
     */
    private boolean isOverFrequent(String mobile, String usage) {
        boolean overFrequent = false;
        Captcha byUsage = captchaDao.findLatestByUsage(mobile, usage);
        if (byUsage != null) {
            Date sendTime = byUsage.getSendTime();
            Date now = new Date();
            if (now.getTime() - sendTime.getTime() < Consts.DEFAULT_SMS_SEND_INTERVAL) {
                overFrequent = true;
            }
        }
        return overFrequent;
    }

    @Override
    @Transactional(readOnly = false)
    public ServiceResponse validate(String mobile, String usage, String token, String captcha) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Start to validate captcha: {mobile=" + mobile + ",usage=" + usage + ",token=" + token + ",captcha=" + captcha + "}");
        }
        ServiceResponse fail = ServiceResponse.fail(Consts.i18nKey.INVALID_CAPTCHA);
        if (StringUtils.isBlank(token)) {
            return fail;
        }
        String created = createToken(mobile, usage, captcha);
        if (!created.equalsIgnoreCase(token)) {
            return fail;
        }

        Captcha fromDB = captchaDao.findLatestByUsage(mobile, usage);
        // captcha not equals
        if (fromDB == null || !captcha.equalsIgnoreCase(fromDB.getCaptcha())) {
            return fail;
        }
        // captcha is used
        if (YesNo.Yes.charVal == fromDB.getUsed()) {
            return fail;
        }
        // update captcha
        captchaDao.delete(fromDB.getId());
        return ServiceResponse.success();
    }

    private String createToken(String mobile, String usage, String captcha) {
        String str = String.format(TOKEN_FORMAT, usage, mobile, captcha);
        return DigestUtils.md5Hex(str);
    }

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void setCaptchaDao(CaptchaDao captchaDao) {
        this.captchaDao = captchaDao;
    }

    public void setSmsClient(SmsClient smsClient) {
        this.smsClient = smsClient;
    }
}
